#ifndef MESSAGESHANDLER_H
#define MESSAGESHANDLER_H

#include "../common/messages.h"
#include "locksmatrix.h"
#include <list>
#include <map>
#include <time.h>

using namespace std;

/*
 * Definicja typu identyfikatora dla zasobu
 */
typedef int RescourceId;

/*
 * Definicja typu identyfikatora dla procesu
 */
typedef int ProcessId;

/*
 * Struktura opisująca blokadę
 */
struct LockDesc_{
    RescourceId resourceId; // id zasobu
    ProcessId processId;    // id procesu
    LockType type;          // typ blokady
    long timeout;           // niedoczas
    bool operator ==(const struct LockDesc_ &p)const;
};
typedef struct LockDesc_ LockDesc;

/*
 * Struktura opisująca proces
 */
struct Process_{
    const char* fifoName;
    int fd;
    ProcessId processId;
    list<RescourceId> allResources;
    bool operator ==(const struct Process_ &p)const;
};
typedef struct Process_ Process;


/*
 * Klasa odpowiedzialna za obsługą wiadomości.
 * Zarządza kolejkami do zasobów.
 * Wysyła odpoowiedzi do klientów.
 */
class MessagesHandler
{
public:
    MessagesHandler();
    /*
     * Obsługuje wiadomości przychodzące do DLM
     */
    void handleMsg(RequestMsg& msg);

    /*
     * Zwraca czas kiedy upłynie najbliższy timeout
     */
    long getTimeout();

    /*
     * Obsługuje zdarzenie związane z upłynięciem timeoutu
     */
    void handleTimeout();

private:
    /*
     * Obsługuje wiadomość OPEN
     */
    void handleOpen(RequestMsg& msg);

    /*
     * Obsługuje wiadomość CLOSE
     */
    void handleClose(RequestMsg& msg);

    /*
     * Obsługuje wiadomość ADD RESOURCE
     */
    void handleAddResource(RequestMsg& msg);

    /*
     * Obsługuje wiadomość DEL RESOURCE
     */
    void handleDelResource(RequestMsg& msg);

    /*
     * Obsługuje wiadomość LOCK
     */
    void handleLock(RequestMsg& msg);

    /*
     * Obsługuje wiadomość TRY LOCK
     */
    void handleTryLock(RequestMsg& msg);

    /*
     * Obsługuje wiadomość UNLOCK
     */
    void handleUnlock(RequestMsg& msg);

    /*
     * Obsługuje wiadomość z błędnym typem
     */
    void handleUnsupportedMsg(RequestMsg& msg);

    /*
     * Obsługuje sytuacje gdy podano błędne id procesu
     */
    void handleUnknownProcessId(ProcessId id);

    /*
     * Zwraca true gdy dana kolejka FIFO jest już otwarta
     */
    bool isFifoOpened(const char* fifoName)const;

    /*
     * Sprawdza czy podany identyfikator zasobu jest prawidłowy.
     * W przypadku gdy subscriber jest różny od NULL, identyfikator
     * resourceId musi być dodatkowo na liście zasobów danego procesu
     */
    bool isValidResourceId(RescourceId resourceId, Process* subscriber = NULL)const;

    /*
     * Sprawdza czy podany identyfikator typu blokady jest prawidłowy
     */
    bool isValidLockType(LockType type)const;

    /*
     * Wysyła ResopnseMsg do podanego procesu, z danym kodem statusu operacji
     */
    void sendResponse(const Process* process, const int status)const;

    /*
     * Wysyła ResopnseMsg do podanego procesu, z danym kodem statusu operacji
     */
    void sendResponse(const ProcessId processId, const int status)const;

    /*
     * Usuwa zasób z listy zasobów danego procesu
     */
    void delResource(RescourceId id, Process *p);

    /*
     * Sprawdza czy można założyć blokade podanego typu na dany zasób
     */
    bool canLock(const RescourceId, const LockType type)const;

    /*
     * Sprawdza czy któryś proces oczekujący na dany zasób może
     * uzyskać do niego dostęp. Gdy istnieje taka możliwość, na zasób
     * zostaje założona oczekująca blokada
     */
    void parseWaiting(const RescourceId id);

    /*
     * Usuwa wszsytkie blokady należące do procesu (gdy id==NULL) lub
     * lub wszystkie blokady procesu p na danym zasobie (gdy id !=NULL)
     */
    void removeProcessFromAllQueues(Process* p, RescourceId* id=NULL);

    /*
     * Zwraca wskazanie na strukture procesu. W przypadku błędnego identyfikatora
     * zwracana jest wartość NULL
     */
    const Process* getProcessC(ProcessId processId)const;
    Process* getProcess(ProcessId processId);

    /*
     * Buduje obiekt blokady na podstawie danych z wiadomości
     */
    LockDesc buildLockDesc(RequestMsg& msg);

    LocksMatrix locksMatrix_; // macierz blokad
    list<Process> processes_; // lista podłączonych procesów
    list<RescourceId> allResources_; // lista wszystkich zasobów
    mutable map<RescourceId, list<LockDesc> > currentLocks_; // mapa id zasobu na listę założonych blokad
    map<RescourceId, list<LockDesc> > waitingLocks_; // mapa id zasobu na listę oczekujących blokad
    list<LockDesc> waitingLocksWithTimeout_; // lista oczekujących blokad z ustawionym timeoutem
    struct timespec lastTime_;  // czas kiedy ostatnio uaktualniono timeout dla oczekujących blokad
};

#endif // MESSAGESHANDLER_H
